<!DOCTYPE html>
<script>
    /*
     * Promise 实现
     * Promise 有3个状态 pending、 resolve 和 reject。因为状态的的确定，所以Promise的结果是可靠的。 */
    /* 
        # JS是单线程语言

        JS的单线程中,任务队列可以有多个,这些队列都是依靠函数调用栈来循环执行，这也就是我们常说的事件轮循。事件轮循决定了代码的执行顺序。

        **任务队列** 包括宏任务（script全局，setTimeout，setInterval）和微任务（Promise）。

        **轮循机制**
        从script(整体代码)开始第一次循环，全局上下文进入函数调用栈（栈底），如果有可执行代码就进行正常的入栈出栈，如果有上面提到的setTimeout和Promise，就将任务分发到各自队列，直到调用栈清空(只剩全局)，然后执行所有的微任务队列（Promise队列），这就是第一次循环。当所有可执行的微任务执行完毕之后，循环再次从宏任务（setTimeout队列）开始执行入栈出栈任务分发等，执行完毕，然后再执行所有的微任务，第二次循环结束。。。这样一直循环下去，直到再也没有可执行的任务。这就是JS的循环机制。

        等到浏览器关闭页面，在栈底的全局上下文才会出栈。

        > 注：setTimeout分发的是内部的函数，如果是立即执行表达式，将会同步执行。而且它执行的时间不是绝对可控的，这要取决于前面代码的执行效率。
    
    */
    // 基础版本
    class Promise {
        constructor(fn) {
            /* 
             * promise有三个状态：
                1、pending[待定]初始状态
                2、fulfilled[实现]操作成功
                3、rejected[被否决]操作失败
                当promise状态发生改变，就会触发then()里的响应函数处理后续步骤；
                promise状态一经改变，不会再变。
              */
            this.state = "pending";
            this.value = undefined;
            this.reason = undefined;
            /* 
             * resolve作用是，将Promise对象的状态从“未完成”变为“成功”（即从 pending 变为 resolved），
             在异步操作成功时调用，并将异步操作的结果，作为参数传递出去；
            */
            let resolve = (value) => {
                if (this.state === "pending") {
                    this.state = "fulfilled";
                    this.value = value;
                }
            };
            /* 
             * reject作用是，将Promise对象的状态从“未完成”变为“失败”（即从 pending 变为 rejected），在异步操作失败时调用，并将异步操作报出的错误，作为参数传递出去。
            */
            let reject = (value) => {
                if (this.state === "pending") {
                    this.state = "rejected";
                    this.reason = value;
                }
            };
            // 自动执行函数
            try {
                fn(resolve, reject);
            } catch (e) {
                reject(e);
            }
        }
        // then
        /* 
         * Promise 对象的状态改变，只有两种可能：
            从 pending 变为 fulfilled
            从 pending 变为 rejected。
            这两种情况只要发生，状态就凝固了，不会再变了。 
          */
        then(onFulfilled, onRejected) {
            switch (this.state) {
                case "fulfilled":
                    onFulfilled(this.value);
                    break;
                case "rejected":
                    onRejected(this.value);
                    break;
                default:
            }
        }
    }

    /* 改良版：yck 小册里面实现的了Promise的主要功能（没有catch、finally、静态调用等） */
    // 三种状态
    const PENDING = "pending";
    const RESOLVED = "resolved";
    const REJECTED = "rejected";

    // promise 接收一个函数参数，该函数会立即执行
    function MyPromise(fn) {
        let _this = this;
        _this.currentState = PENDING;
        _this.value = undefined;
        // 用于保存 then 中的回调，只有当 promise
        // 状态为 pending 时才会缓存，并且每个实例至多缓存一个
        _this.resolvedCallbacks = [];
        _this.rejectedCallbacks = [];

        _this.resolve = function (value) {
            if (value instanceof MyPromise) {
                // 如果 value 是个 Promise，递归执行
                return value.then(_this.resolve, _this.reject);
            }
            setTimeout(() => {
                // 异步执行，保证执行顺序
                if (_this.currentState === PENDING) {
                    _this.currentState = RESOLVED;
                    _this.value = value;
                    _this.resolvedCallbacks.forEach((cb) => cb());
                }
            });
        };

        _this.reject = function (reason) {
            setTimeout(() => {
                // 异步执行，保证执行顺序
                if (_this.currentState === PENDING) {
                    _this.currentState = REJECTED;
                    _this.value = reason;
                    _this.rejectedCallbacks.forEach((cb) => cb());
                }
            });
        };
        // 用于解决以下问题
        // new Promise(() => throw Error('error))
        try {
            fn(_this.resolve, _this.reject);
        } catch (e) {
            _this.reject(e);
        }
    }

    MyPromise.prototype.then = function (onResolved, onRejected) {
        var self = this;
        // 规范 2.2.7，then 必须返回一个新的 promise
        var promise2;
        // 规范 2.2.onResolved 和 onRejected 都为可选参数
        // 如果类型不是函数需要忽略，同时也实现了透传
        // Promise.resolve(4).then().then((value) => console.log(value))
        onResolved = typeof onResolved === "function" ? onResolved : (v) => v;
        onRejected =
            typeof onRejected === "function"
                ? onRejected
                : (r) => {
                      throw r;
                  };
        if (self.currentState === RESOLVED) {
            return (promise2 = new MyPromise(function (resolve, reject) {
                // 规范 2.2.4，保证 onFulfilled，onRjected 异步执行
                // 所以用了 setTimeout 包裹下
                setTimeout(function () {
                    try {
                        var x = onResolved(self.value);
                        resolutionProcedure(promise2, x, resolve, reject);
                    } catch (reason) {
                        reject(reason);
                    }
                });
            }));
        }

        if (self.currentState === REJECTED) {
            return (promise2 = new MyPromise(function (resolve, reject) {
                setTimeout(function () {
                    // 异步执行onRejected
                    try {
                        var x = onRejected(self.value);
                        resolutionProcedure(promise2, x, resolve, reject);
                    } catch (reason) {
                        reject(reason);
                    }
                });
            }));
        }

        if (self.currentState === PENDING) {
            return (promise2 = new MyPromise(function (resolve, reject) {
                self.resolvedCallbacks.push(function () {
                    // 考虑到可能会有报错，所以使用 try/catch 包裹
                    try {
                        var x = onResolved(self.value);
                        resolutionProcedure(promise2, x, resolve, reject);
                    } catch (r) {
                        reject(r);
                    }
                });

                self.rejectedCallbacks.push(function () {
                    try {
                        var x = onRejected(self.value);
                        resolutionProcedure(promise2, x, resolve, reject);
                    } catch (r) {
                        reject(r);
                    }
                });
            }));
        }
    };

    // 规范 2.3
    function resolutionProcedure(promise2, x, resolve, reject) {
        // 规范 2.3.1，x 不能和 promise2 相同，避免循环引用
        if (promise2 === x) {
            return reject(new TypeError("Error"));
        }
        // 规范 2.3.2
        // 如果 x 为 Promise，状态为 pending 需要继续等待否则执行
        if (x instanceof MyPromise) {
            if (x.currentState === PENDING) {
                x.then(function (value) {
                    // 再次调用该函数是为了确认 x resolve 的
                    // 参数是什么类型，如果是基本类型就再次 resolve
                    // 把值传给下个 then
                    resolutionProcedure(promise2, value, resolve, reject);
                }, reject);
            } else {
                x.then(resolve, reject);
            }
            return;
        }
        // 规范 2.3.3.3.3
        // reject 或者 resolve 其中一个执行过得话，忽略其他的
        let called = false;
        // 规范 2.3.3，判断 x 是否为对象或者函数
        if (x !== null && (typeof x === "object" || typeof x === "function")) {
            // 规范 2.3.3.2，如果不能取出 then，就 reject
            try {
                // 规范 2.3.3.1
                let then = x.then;
                // 如果 then 是函数，调用 x.then
                if (typeof then === "function") {
                    // 规范 2.3.3.3
                    then.call(
                        x,
                        (y) => {
                            if (called) return;
                            called = true;
                            // 规范 2.3.3.3.1
                            resolutionProcedure(promise2, y, resolve, reject);
                        },
                        (e) => {
                            if (called) return;
                            called = true;
                            reject(e);
                        }
                    );
                } else {
                    // 规范 2.3.3.4
                    resolve(x);
                }
            } catch (e) {
                if (called) return;
                called = true;
                reject(e);
            }
        } else {
            // 规范 2.3.4，x 为基本类型
            resolve(x);
        }
    }

    /* 补充版 */
    // 三种状态
    const PENDING = "pending";
    const RESOLVED = "resolved";
    const REJECTED = "rejected";

    function MyPromise(fn) {
        let _this = this;
        _this.currentState = PENDING;
        _this.value = undefined;
        _this.resolvedCallbacks = [];
        _this.rejectedCallbacks = [];

        _this.resolve = function (value) {
            if (value instanceof MyPromise) {
                return value.then(_this.resolve, _this.reject);
            }
            setTimeout(() => {
                if (_this.currentState === PENDING) {
                    _this.currentState = RESOLVED;
                    _this.value = value;
                    _this.resolvedCallbacks.forEach((cb) => cb());
                }
            });
        };

        _this.reject = function (reason) {
            setTimeout(() => {
                if (_this.currentState === PENDING) {
                    _this.currentState = REJECTED;
                    _this.value = reason;
                    _this.rejectedCallbacks.forEach((cb) => cb());
                }
            });
        };
        try {
            fn(_this.resolve, _this.reject);
        } catch (e) {
            _this.reject(e);
        }
    }

    MyPromise.prototype.then = function (onResolved, onRejected) {
        var self = this;
        var promise2;
        onResolved = typeof onResolved === "function" ? onResolved : (v) => v;
        onRejected =
            typeof onRejected === "function"
                ? onRejected
                : (r) => {
                      throw r;
                  };

        if (self.currentState === RESOLVED) {
            return (promise2 = new MyPromise(function (resolve, reject) {
                setTimeout(function () {
                    try {
                        var x = onResolved(self.value);
                        resolutionProcedure(promise2, x, resolve, reject);
                    } catch (reason) {
                        reject(reason);
                    }
                });
            }));
        }

        if (self.currentState === REJECTED) {
            return (promise2 = new MyPromise(function (resolve, reject) {
                setTimeout(function () {
                    try {
                        var x = onRejected(self.value);
                        resolutionProcedure(promise2, x, resolve, reject);
                    } catch (reason) {
                        reject(reason);
                    }
                });
            }));
        }

        if (self.currentState === PENDING) {
            return (promise2 = new MyPromise(function (resolve, reject) {
                self.resolvedCallbacks.push(function () {
                    try {
                        var x = onResolved(self.value);
                        resolutionProcedure(promise2, x, resolve, reject);
                    } catch (r) {
                        reject(r);
                    }
                });

                self.rejectedCallbacks.push(function () {
                    try {
                        var x = onRejected(self.value);
                        resolutionProcedure(promise2, x, resolve, reject);
                    } catch (r) {
                        reject(r);
                    }
                });
            }));
        }
    };

    // 规范 2.3
    function resolutionProcedure(promise2, x, resolve, reject) {
        if (promise2 === x) {
            return reject(new TypeError("Error"));
        }
        if (x instanceof MyPromise) {
            if (x.currentState === PENDING) {
                x.then(function (value) {
                    resolutionProcedure(promise2, value, resolve, reject);
                }, reject);
            } else {
                x.then(resolve, reject);
            }
            return;
        }
        let called = false;
        if (x !== null && (typeof x === "object" || typeof x === "function")) {
            try {
                let then = x.then;
                if (typeof then === "function") {
                    then.call(
                        x,
                        (y) => {
                            if (called) return;
                            called = true;
                            resolutionProcedure(promise2, y, resolve, reject);
                        },
                        (e) => {
                            if (called) return;
                            called = true;
                            reject(e);
                        }
                    );
                } else {
                    resolve(x);
                }
            } catch (e) {
                if (called) return;
                called = true;
                reject(e);
            }
        } else {
            resolve(x);
        }
    }

    // catch方法
    MyPromise.prototype.catch = function (rejectFn) {
        return this.then(undefined, rejectFn);
    };

    //finally方法
    MyPromise.prototype.finally = function (callback) {
        return this.then(
            (value) => MyPromise.resolve(callback()).then(() => value),
            (reason) =>
                MyPromise.resolve(callback()).then(() => {
                    throw reason;
                })
        );
    };

    /* 
        静态方法添加
    */

    // resolve方法
    MyPromise.resolve = function (val) {
        return new MyPromise((resolve, reject) => {
            resolve(val);
        });
    };

    //reject方法
    MyPromise.reject = function (val) {
        return new MyPromise((resolve, reject) => {
            reject(val);
        });
    };

    //race方法
    MyPromise.race = function (promises) {
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(resolve, reject);
            }
        });
    };

    //all方法(获取所有的promise，都执行then，把结果放到数组，一起返回)
    MyPromise.all = function (promises) {
        let arr = [];
        let i = 0;

        function processData(index, data) {
            arr[index] = data;
            i++;
            if (i == promises.length) {
                resolve(arr);
            }
        }

        return new Promise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then((data) => {
                    processData(i, data);
                }, reject);
            }
        });
    };
</script>
